﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Xml;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

namespace WIAssistant
{
    public class MainMediator: DependencyObject
    {
        public TeamFoundationServer TFSServer { get; set; }
        public WorkItemStore WIStore { get; set; }
        public ICommonStructureService CommonStructService { get; set; }
        public IMain Form;

        private bool _connectedToTFS;
        public bool ConnectedToTFS { get { return _connectedToTFS; } }

        public List<string> ServerNames { get; set; }
        public List<ProjectInfo> ProjectList { get; set; }

        public List<String> TeamStoredQueryNames { get; set; }
        public List<String> PersonalStoredQueryNames { get; set; }
        public StoredQueryCollection AllStoredQueries { get; set; }
        public WorkItemCollection QueryResults { get; set; }
        public Dictionary<WorkItem, Canvas> Pad { get; set; }

        public string CurrentQuery { get; private set; }

        public List<int> WorkItemsIDsToDisable { get; set; }

        private static string _comment;
        
        private static MainMediator _instance;
        private static readonly object _padlock = new object();


        public MoveWorkItemsCommand MoveWorkItemCommand { get; set; }

        #region Constructor

        // DO NOT USE!!!!
        public MainMediator()
        {

        }

        public MainMediator(IMain main)
        {
            Form = main;
            // When comparing for our dictionary just use the Id value (even if we are doing a hash compare).
            Pad = new Dictionary<WorkItem, Canvas>(new LambdaComparer<WorkItem>((x, y) => x.Id == y.Id, x => x.Id));

            WorkItemsIDsToDisable = new List<int>();

            MoveModeSelected = false;

            _backgroundWorker = new BackgroundWorker();
            _backgroundWorker.DoWork += BackgroundWorkerDoWork;
            //_backgroundWorker.ProgressChanged += BackgroundWorkerProgressChanged;
            _backgroundWorker.RunWorkerCompleted += BackgroundWorkerRunWorkerCompleted;
            //_backgroundWorker.WorkerReportsProgress = true;


            // Make the commands

            // Move Work Item Command
            MoveWorkItemCommand = new MoveWorkItemsCommand(this);

            
        }

        public static MainMediator Instance
        {
            get
            {
                lock (_padlock)
                {
                    if (_instance == null)
                    {
                        _instance = new MainMediator(Main.Instance);
                    }
                    return _instance;
                }
            }
        }

        public static void SetInstance(MainMediator mainMediator)
        {
            lock (_padlock)
            {
                _instance = mainMediator;
            }
        } 
        #endregion
        
        #region Pad And GUI
        public void ClearAll()
        {
            ClearProject();

            _connectedToTFS = false;
            Form.CurrentServer = null;
            Form.ServerListSource = null;

            ServerNames = null;
        }


        public void ClearProject()
        {
            _connectedToTFS = false;
            Form.CurrentProject = null;
            Form.ProjectListSource = null;
            TeamStoredQueryNames = null;
            PersonalStoredQueryNames = null;
            AllStoredQueries = null;


            ProjectList = null;
        }

        private WorkItem _pickedWorkItem;
        public WorkItem PickedWorkItem
        {
            get
            {
                return _pickedWorkItem;
            }
            set
            {
                _pickedWorkItem = value;
                UpdatePickedWorkItem();
            }
        }

        // Save off the list because the visibility has changed
        public void WorkItemColumnTypeOnVisibleChanged(int fieldID, Visibility visibility)
        {
            // Find all the Display Fields that should have their visibility changed and change it.
            //QueryDisplay.ForEach(row => row.FindAll(column => column.FieldID == fieldID)
            //                                .ForEach(column => column.ItemVisibility = visibility));
            foreach (List<WorkItemColumnItem> row in QueryDisplay)
            {
                foreach (WorkItemColumnItem columnItem in row)
                {
                    if (columnItem.FieldID == fieldID)
                    {
                        columnItem.ItemVisibility = visibility;
                    }
                }
            }

            ColumnFields.SerializeObject(ColumnFields.GetSerializeFilename());
        }

        /// <summary>
        /// Create a link between the item "Picked" and the items in the Pad.
        /// </summary>
        public void LinkPickedAndPad(bool saveLinks, RoutedEventArgs args, string comment)
        {
            if (saveLinks && (!PickedWorkItem.IsOpenInAnyWay()))
                PickedWorkItem.Open();
            else
                PickedWorkItem.SyncToLatest();

            Grid parentGrid = (Grid)((Button)args.Source).Parent;

            _comment = comment;

            // Animate that the link is being made
            ContentControl dest = (ContentControl)parentGrid.FindName("AnimationDestination");
            IEnumerable<WorkItem> itemsToDelselect = new List<WorkItem>();
            Form.AnimatePaneBox(dest, Form.CanvasPad.Children, OnLinkAnimationFinished, parentGrid, itemsToDelselect);

            List<WorkItem> workItems = Pad.Keys.ToList();

            StartWaitCursor();
            _backgroundWorker.RunWorkerAsync(workItems);


            // Clear the Pad
            foreach (WorkItem item in workItems)
            {
                Pad.RemoveWorkItemFromPad(item, Form);
            }

            List<ListBoxItem> listBoxItems = new List<ListBoxItem>();
            foreach (IEnumerable<WorkItemColumnItem> boxItem in Main.Instance.lstQueryResults.SelectedItems)
            {
                listBoxItems.Add((ListBoxItem)Main.Instance.lstQueryResults.ItemContainerGenerator.ContainerFromItem(boxItem));
            }
            foreach (ListBoxItem item in listBoxItems)
            {
                if (item != null)
                {
                    item.IsSelected = false;
                    item.IsEnabled = false;
                }
            }

        } 
        #endregion
        
        #region Move WorkItem Code

        #region Pick Move Destination
        public void FillIterationSelectorWithProjects(List<ProjectInfo> projects)
        {
            IterationTree = new ObservableCollection<IterationNode>();
            projects.ForEach(project => IterationTree.Add(
                                            new IterationNode
                                                {
                                                    IterationPath = project.Name,
                                                    Uri = project.Uri,
                                                    ProjectInfo = project,
                                                    IsProjectLevel = true
                                                }));

        }

        public void IterationSelectorItemChanged(IterationNode node, RoutedPropertyChangedEventArgs<object> args)
        {
            SelectedIterationNode = node;
            if (node.IsProjectLevel)
                EnumerateIterationsForProject(node);
        }

        public ObservableCollection<IterationNode> EnumerateIterationsForProject(IterationNode currentProject)
        {
            // This should never happen, but if it does then we need a valid IterationTree object.
            if (IterationTree == null)
                IterationTree = new ObservableCollection<IterationNode>(); ;

            NodeInfo[] nodeInfos = CommonStructService.ListStructures(currentProject.Uri);
            foreach (NodeInfo node in nodeInfos)
            {
                if (node.StructureType == "ProjectLifecycle")
                {
                    XmlElement element = CommonStructService.GetNodesXml(new[] { node.Uri }, true);
                    AddChildNodes(node.Name, element.ChildNodes[0], currentProject.Children, currentProject.ProjectInfo);
                }
            }
            return IterationTree;
        }

        static void AddChildNodes(string parentPath, XmlNode parentNode, ICollection<IterationNode> results, ProjectInfo projectInfo)
        {
            string trimmedParentPath = parentPath.TrimStart(new char[] {'\\'});

            // This indirection allows us to skip nodes that are not really part of the path (ie the "Iteration" node)
            ICollection<IterationNode> childrenCollection;

            // Trim off the extra stuff (if it does not start with the project then it is not a path
            if (! trimmedParentPath.StartsWith(projectInfo.Name))
            {
                childrenCollection = results;
            }
            else
            {
                // Remove "//Iteration" from the path.  This is part of the object structure but does not make for valid iteration paths.
                const string removeString = "\\Iteration";
                int index = trimmedParentPath.IndexOf(removeString);
                string cleanPath = (index < 0)
                    ? trimmedParentPath
                    : trimmedParentPath.Remove(index, removeString.Length);
                
                IterationNode newNode = new IterationNode()
                {
                    IterationPath = cleanPath,
                    Uri = parentNode.Attributes["NodeID"].Value,
                    ProjectInfo = projectInfo,
                    IsProjectLevel = false
                };

                results.Add(newNode);
                childrenCollection = newNode.Children;
            }
            
            if (parentNode.ChildNodes[0] == null)
                return;

            foreach (XmlNode node in parentNode.ChildNodes[0].ChildNodes)
            {
                string nodePath = node.Attributes["Path"].Value;
                AddChildNodes(nodePath, node, childrenCollection, projectInfo);
            }
        }

        public void ClearSelectedMoveIteration()
        {
            SelectedIterationNode = null;
        }

        #endregion Pick Move Destination

        #region Setup Move Mapping Types

        public static void OnMoveDestSelected()
        {
            // If we did not get a destination then we are done here
            if (Instance.SelectedIterationNode == null)
                return;

            // Get the destination project from the selected iteration.
            Project destProject = Instance.GetProjectFromInfo(Instance.SelectedIterationNode.ProjectInfo);

            // Generate the mappings (What we can at least.)
            SetupMappings();

            // Create the list of WorkItemTypes to choose from.
            var tempTypeList = new List<WorkItemType>();
            foreach (WorkItemType type in destProject.WorkItemTypes)
            {
                tempTypeList.Add(type);
            }

            Instance.WorkItemTypesForCurrentDestProject = tempTypeList;
        }

        private static void SetupMappings()
        {
            Instance.WorkItemTypeMappings = new ObservableCollection<WorkItemTypeMapping>();

            // Iterate each item in the pad to find the unique WorkItemTypes
            foreach (WorkItem item in Instance.Pad.Keys)
            {
                AddMappingForWorkItem(item);
            }
        }

        /// <summary>
        /// If not present then add the mapping for the passed in work item to the source project.
        /// </summary>
        /// <param name="item">Item to try to add a mapping for</param>
        public static void AddMappingForWorkItem(WorkItem item)
        {
            Project destProject = Instance.GetProjectFromInfo(Instance.SelectedIterationNode.ProjectInfo);
            Project sourceProject = item.Project;
            WorkItemType sourceType = item.Type;

            // Create a mapping to compare against.
            WorkItemTypeMapping newMapping = new WorkItemTypeMapping
                                                  {
                                                      SourceProject = sourceProject,
                                                      SourceType = sourceType
                                                  };

            // If this mapping is not in the list already, then we need to add it.
            if (Instance.WorkItemTypeMappings.Count(mapping => mapping == newMapping) == 0)
            {
                // If we don't have this mapping yet, fill it up and then add it to the list

                newMapping.DestProject = destProject;

                // Is if the dest Project has a work Item Type that matches source type.  If it does then try to auto fill what we can.
                foreach (WorkItemType possibleDestType in newMapping.DestProject.WorkItemTypes)
                {
                    if (possibleDestType.Name == sourceType.Name)
                    {
                        // We have a match.  It may not be the one the user wants, but lets try it.
                        //  Setting this value will cause newMapping to try and automap as best it can.
                        newMapping.DestType = possibleDestType;
                        break;
                    }
                }

                Instance.WorkItemTypeMappings.Add(newMapping);
            }
        }

        public void ManualMappingClick(WorkItemTypeMapping mapping)
        {
            //ObservableCollection<WorkItem> workItems = new ObservableCollection<WorkItem>();
            //Pad.Keys.Where(x=>x.Type == mapping.SourceType).ToList() .ForEach(workItems.Add);

            ManualMapping manualMapping = new ManualMapping(mapping);
            manualMapping.ShowDialog();
        }
        #endregion Setup Move Mapping Types

        #endregion Move WorkItem Code
        
        #region Animation Of Pad

        private readonly BackgroundWorker _backgroundWorker;

        // Get a list of registered servers
        private List<TeamFoundationServer> GetTFSServers()
        {
            return RegisteredServers.GetServers().ToList();

        }

        void BackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            List<WorkItem> workItems = (List<WorkItem>)e.Result;


            EndWaitCursor();


            // TODO.  Log/show that e.Result work items did not get linked becaue they are already linked.
        }

        //void BackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
        //{
        //    Form.WaitCursorText.Text = e.ProgressPercentage.ToString() + "%";
        //}

        private void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
        {
            // Get the BackgroundWorker that raised this event.
            BackgroundWorker worker = sender as BackgroundWorker;

            List<WorkItem> workItems = (List<WorkItem>)e.Argument;
            CreateLinks((List<WorkItem>)e.Argument, worker, e);

            // Assign the list of work items to the result so it is 
            // available to us in the complete method.
            e.Result = workItems;
        }

        public void StartWaitCursor()
        {
            Form.WaitCursorText.Text = "";
            Form.WaitCursorVisibility = Visibility.Visible;
        }

        public void EndWaitCursor()
        {
            Form.WaitCursorText.Text = "";
            Form.WaitCursorText.Visibility = Visibility.Hidden;
            Form.WaitCursorVisibility = Visibility.Hidden;
        }

        public void WaitCursorMessage(string message)
        {
            Form.WaitCursorText.TextWrapping = TextWrapping.Wrap;
            Form.WaitCursorText.Visibility = Visibility.Visible;
            Form.WaitCursorText.Text = message;
        }

        private List<WorkItem> CreateLinks(IEnumerable<WorkItem> workItems, BackgroundWorker worker, DoWorkEventArgs args)
        {
            List<WorkItem> itemsFailedToLink = new List<WorkItem>();
            foreach (WorkItem workItem in workItems)
            {
                RelatedLink link = new RelatedLink(workItem.Id) { Comment = _comment };
                try
                {
                    PickedWorkItem.Links.Add(link);
                }
                catch (Exception)
                {
                    itemsFailedToLink.Add(workItem);
                }

                workItem.PartialOpen();
                workItem.Save();
                PickedWorkItem.Save();
            }
            //worker.ReportProgress(100);
            return itemsFailedToLink;
        }


        // For some unknown reason, if the code in this is run before the animation is finished, the animation will
        //  not run.
        public void OnLinkAnimationFinished(FrameworkElement parent, IEnumerable<WorkItem> itemsToDeselect)
        {
            ItemsControl linkListUI = Form.GetWILinkElement<ItemsControl>("lstLinkList");

            //ItemsControl linkListUI = ((ItemsControl)parentGrid.FindName("lstLinkList"));
            BindingExpression bindingExpr = BindingOperations.GetBindingExpression(linkListUI, ItemsControl.ItemsSourceProperty);
            if (bindingExpr != null)
                bindingExpr.UpdateTarget();  // refreshes the ItemsSource

            List<ListBoxItem> listBoxItems = new List<ListBoxItem>();
            foreach (IEnumerable<WorkItemColumnItem> boxItem in Main.Instance.lstQueryResults.SelectedItems)
            {
                listBoxItems.Add((ListBoxItem)Main.Instance.lstQueryResults.ItemContainerGenerator.ContainerFromItem(boxItem));
            }
            foreach (ListBoxItem item in listBoxItems)
            {
                if (item != null)
                {
                    item.IsSelected = false;
                    item.IsEnabled = false;
                }
            }


        } 
        #endregion
        
        #region TFS Stuff

        // Get a list of registered servers
        public List<String> GetTFSServerNames()
        {
            if (ServerNames == null)
                ServerNames = (from servers in RegisteredServers.GetServers()
                               select servers.Name).ToList();

            return ServerNames;

        }

        public void UpdatePickedWorkItem()
        {
            // Setup the list of items that we want to disable when they are in the list
            // Also pull any items in the links list off the Pad
            WorkItemsIDsToDisable.Clear();
            // We need to disable any items that are already links
            foreach (Link link in _pickedWorkItem.Links)
            {
                if (link.BaseType == BaseLinkType.RelatedLink)
                {
                    int relatedWorkItemID = ((RelatedLink)link).RelatedWorkItemId;
                    WorkItemsIDsToDisable.Add(relatedWorkItemID);
                    // We don't want any Items in the Disabled list to be on the pad, though most of the
                    //  time they will be pulled off because they are not selected anymore.  Sometimes
                    //  the user can select items from more than one query.  In that case this is needed.
                    Pad.RemoveWorkItemFromPad(relatedWorkItemID, Form);
                }
            }
            // Making a link to your self does not make much sense.  Disable that and remove it from the Pad too.
            WorkItemsIDsToDisable.Add(_pickedWorkItem.Id);
            Pad.RemoveWorkItemFromPad(_pickedWorkItem.Id, Form);
        }

        // Get a list of all the Projects from the current TFS Server
        public List<ProjectInfo> GetTFSProjects()
        {
            // If we have not connected to a TFS server then we cannot get a list of projects.
            if (!_connectedToTFS)
            {
                MessageBox.Show("Please Connect to TFS before working with TFS Projects");
                return null;
            }

            if (ProjectList == null)
                ProjectList = CommonStructService.ListAllProjects().OrderBy(p => p.Name).ToList();

            return ProjectList;
        }

        // User has selected the server they want to work with.  Authenticate and fill the project list.
        public void ServerSelected()
        {
            AuthenticateToServer(Form.CurrentServer);
            var projects = GetTFSProjects();
            Form.ProjectListSource = projects;

            SetupDefaultFields(projects, Form.CurrentServer);
            // Fill the move list too
            FillIterationSelectorWithProjects(projects);
        }

        private void SetupDefaultFields(List<ProjectInfo> projects, string server)
        {
            // Create the default column tree (Load if there or make a new on if it is not there).
            WITSelectorProjects = MiscHelpers.DeserializeObject<ObservableCollection<WITSelectorProject>>(server + "DefaultFields.xml");
            
            if (WITSelectorProjects == null)
            {
                WITSelectorProjects = new ObservableCollection<WITSelectorProject>();
            }

            // Setup the list of projects for the settings section.  This is the part where the user picks the default column.
            //projects.ForEach(projectInfo => WITSelectorProjects.Add(new WITSelectorProject { WITProjectName = projectInfo.Name }));

            projects.ForEach(projectInfo =>
                {
                    // See if this project is in the list.
                    bool projectExists =
                        WITSelectorProjects.Where(proj => proj.WITProjectName == projectInfo.Name).Count() > 0;

                    if (!projectExists)
                        WITSelectorProjects.Add(new WITSelectorProject { WITProjectName = projectInfo.Name });
                });
        }
        // Fill up the UI list with all the available TFS Servers
        public void FillServerList()
        {
            Form.ServerListSource = GetTFSServerNames();
        }

        // Authentication to the server.  Also setup some constructs used later on.
        private bool AuthenticateToServer(string server)
        {
            try
            {
                // Connect to the TFS Server and make sure we are authenticated.
                UICredentialsProvider uiCreds = new UICredentialsProvider();
                TFSServer = new TeamFoundationServer(server, uiCreds);
                TFSServer.EnsureAuthenticated();

                WIStore = (WorkItemStore)TFSServer.GetService(typeof(WorkItemStore));
                CommonStructService = (ICommonStructureService)TFSServer.GetService(typeof(ICommonStructureService));

                _connectedToTFS = true;
                return true;
            }
            catch (Exception)
            {
                _connectedToTFS = false;
                MessageBox.Show("Could not connect to Team Foundation Server", "Error");
                return false;
            }
        }

        public void FillQueryListsForProject(ProjectInfo projectInfo)
        {

            TeamStoredQueryNames = new List<string>();
            PersonalStoredQueryNames = new List<string>();

            StoredQueryCollection storedQueries = GetProjectFromInfo(projectInfo).StoredQueries;

            AllStoredQueries = storedQueries;

            foreach (StoredQuery query in storedQueries)
            {
                if (query.QueryScope == QueryScope.Public)
                    TeamStoredQueryNames.Add(query.Name);
                else if (query.QueryScope == QueryScope.Private)
                    PersonalStoredQueryNames.Add(query.Name);
            }
        }

        public void ProjectSelected()
        {
            FillQueryListsForProject(Form.CurrentProject);
            Form.PersonalQueryNamesListSource = PersonalStoredQueryNames;
            Form.TeamQueryNamesListSource = TeamStoredQueryNames;
        }

        public void QuerySelected(object sender)
        {
            
            ListBox selectionBox = (ListBox)sender;
            CurrentQuery = selectionBox.SelectedItem.ToString();

            RunQuery(CurrentQuery);
        }

        /// <summary>
        /// Given the query name, find the query and run it and display its results.
        /// </summary>
        /// <param name="nameOfQueryToRun"></param>
        public void RunQuery(string nameOfQueryToRun)
        {
            string queryToRun = null;

            foreach (StoredQuery query in AllStoredQueries)
            {
                if (query.Name == nameOfQueryToRun)
                {
                    queryToRun = query.QueryText;
                }
            }


            if (queryToRun != null)
            {
                // Get the list of parameters
                List<StringPair> parsedParams =
                    // Break the query up into words
                    queryToRun.Split(new[] { '.', '?', '!', ' ', ';', ':', ',' }, StringSplitOptions.RemoveEmptyEntries)
                    // Find all the words that begin with @
                        .ToList().FindAll(str => str[0] == '@').Distinct()
                    // Output the results as a list of StringPairs
                        .Select(str => new StringPair { Key = str, Value = "" }).ToList();

                // We want to handle the project parameter as a special case.
                // This is a list but it should really only have 0 or 1 values.
                // If the user had different case however, there may be more.
                List<StringPair> projectParam = parsedParams.FindAll(x => String.Compare(x.Key, "@project", true) == 0);

                // We know the project.  Fill it in if it is one of the variables
                projectParam.ForEach(x => x.Value = (Form.CurrentProject.Name));

                // Transfer everthing to an observiable list
                ObservableCollection<StringPair> observableParams = new ObservableCollection<StringPair>(parsedParams);

                // Since we know the project, see if that is the only variable
                // Compare the key with @project (case insensitive) and if there is more than one
                // ask the user to fill in the params.
                if (parsedParams.Count(x => String.Compare(x.Key, "@project", true) != 0) > 0)
                {
                    // Since there are other variables besides @project ask the user to supply them
                    QueryParams queryParams = new QueryParams { ViewModel = { Parameters = observableParams } };
                    //parsedParams.ForEach(item => queryParams.ViewModel.Parameters.Add(item));
                    queryParams.ShowDialog();
                }

                // Replace any parameters found
                foreach (StringPair parameter in observableParams)
                {
                    queryToRun = queryToRun.Replace(parameter.Key, "'" + parameter.Value + "'");
                }


                // Run the query
                var querySucceeded = false;
                try
                {
                    QueryResults = WIStore.Query(queryToRun);
                    querySucceeded = true;
                }
                catch (Exception e)
                {
                    MessageBox.Show("Error occurred while running query: " + e.Message, "Query Error");
                }

                if (querySucceeded)
                    ChangeQueryResultListSource(QueryResults);
            }
        }
        private void ChangeQueryResultListSource(WorkItemCollection queryResults)
        {
            // Un-hook the selection event while we change the ItemsSource
            Form.ToggleOnSelectEvent(false);

            // Get the new query fields to show.
            ColumnFields = queryResults.GetColumnTitles(CurrentQuery, Form.CurrentProject.Name, Form.CurrentServer);

            // Change the source
            QueryDisplay = queryResults.GetDisplayValues(ColumnFields);
            Form.QueryResultListSource = QueryDisplay;


            // Rewire the selection events back in
            Form.ToggleOnSelectEvent(true);


            foreach (WorkItem item in Pad.Keys)
            {
                Form.SelectQueryResultItem(item);
            }
        }



        public Project GetProjectFromInfo(ProjectInfo projectInfo)
        {
            foreach (Project project in WIStore.Projects)
            {
                if (project.Name == projectInfo.Name)
                    return project;
            }
            return null;
        }

        // Gets a work item from TFS by ID.  This is needed by a converter.
        public WorkItem GetWorkItemByID(int workItemID)
        {
            return WIStore.GetWorkItem(workItemID);
        }

        public void PickWorkItemClicked(RoutedEventArgs e)
        {
            if (e.OriginalSource.GetType().ToString() == "System.Windows.Controls.Button")
            {
                int id = int.Parse(((Button)e.OriginalSource).Tag.ToString());

                PickedWorkItem = Form.GetWorkItemInQueryResultListByID(id);
                Form.TabAddLinksDataContext = PickedWorkItem;
                Form.TabAddLinksContent = PickedWorkItem;

                //PickedWorkItemTitle = _mediator.PickedWorkItem.Title;
                Form.PickedWorkItemID = PickedWorkItem.Id;
            }
        }

        public void QueryResultsSelectionChanged(SelectionChangedEventArgs e)
        {
            foreach (IEnumerable<WorkItemColumnItem> workItemColumns in e.AddedItems)
            {
                if (workItemColumns.Count() > 0)
                    Pad.AddWorkItemToPad(workItemColumns.First().WorkItemForColumn, Form);
            }

            foreach (IEnumerable<WorkItemColumnItem> workItemColumns in e.RemovedItems)
            {
                if (workItemColumns.Count() > 0)
                    Pad.RemoveWorkItemFromPad(workItemColumns.First().WorkItemForColumn, Form);
            }
        } 
        #endregion

        #region MVVM Refactored

        public Visibility VisibilityOfWorkItemColumnTitles
        {
            get { return (Visibility)GetValue(VisibilityOfWorkItemColumnTitlesProperty); }
            set { SetValue(VisibilityOfWorkItemColumnTitlesProperty, value); }
        }
        public static readonly DependencyProperty VisibilityOfWorkItemColumnTitlesProperty = DependencyProperty.Register("VisibilityOfWorkItemColumnTitles", typeof(Visibility), typeof(MainMediator), new UIPropertyMetadata(null));


        public List<List<WorkItemColumnItem>> QueryDisplay
        {
            get { return (List<List<WorkItemColumnItem>>)GetValue(QueryDisplayProperty); }
            set { SetValue(QueryDisplayProperty, value); }
        }
        public static readonly DependencyProperty QueryDisplayProperty = DependencyProperty.Register("QueryDisplay", typeof(List<List<WorkItemColumnItem>>), typeof(MainMediator), new UIPropertyMetadata(null));
        

        public WorkItemColumnTypes ColumnFields
        {
            get { return (WorkItemColumnTypes)GetValue(ColumnFieldsProperty); }
            set { SetValue(ColumnFieldsProperty, value); }
        }
        public static readonly DependencyProperty ColumnFieldsProperty = DependencyProperty.Register("ColumnFields", typeof(WorkItemColumnTypes), typeof(MainMediator), new UIPropertyMetadata(null));

        
        public bool MoveModeSelected
        {
            get { return (bool)GetValue(MoveModeSelectedProperty); }
            set {SetValue(MoveModeSelectedProperty, value);}
        }
        public static readonly DependencyProperty MoveModeSelectedProperty = DependencyProperty.Register("MoveModeSelected", typeof(bool), typeof(MainMediator), new UIPropertyMetadata(false));


        public ObservableCollection<IterationNode> IterationTree
        {
            get { return (ObservableCollection<IterationNode>)GetValue(IterationTreeProperty); }
            set { SetValue(IterationTreeProperty, value); }
        }
        public static readonly DependencyProperty IterationTreeProperty = DependencyProperty.Register("IterationTree", typeof(ObservableCollection<IterationNode>), typeof(MainMediator), new UIPropertyMetadata(null));
        

        public IterationNode SelectedIterationNode
        {
            get { return (IterationNode)GetValue(SelectedIterationNodeProperty); }
            set { SetValue(SelectedIterationNodeProperty, value); }
        }
        public static readonly DependencyProperty SelectedIterationNodeProperty = DependencyProperty.Register("SelectedIterationNode", typeof(IterationNode), typeof(MainMediator), new UIPropertyMetadata(null));

        // If a move iteration has been selected then this is Visible.
        public Visibility ShowMoveTypes
        {
            get { return (Visibility)GetValue(ShowMoveTypesProperty); }
            set { SetValue(ShowMoveTypesProperty, value); }
        }
        public static readonly DependencyProperty ShowMoveTypesProperty = DependencyProperty.Register("ShowMoveTypes", typeof(Visibility), typeof(MainMediator), new UIPropertyMetadata(Visibility.Collapsed));


        // The mappings used to copy the values of the fields of one WorkItem to another.
        public ObservableCollection<WorkItemTypeMapping> WorkItemTypeMappings
        {
            get { return (ObservableCollection<WorkItemTypeMapping>)GetValue(WorkItemTypeMappingsProperty); }
            set { SetValue(WorkItemTypeMappingsProperty, value); }
        }
        public static readonly DependencyProperty WorkItemTypeMappingsProperty = DependencyProperty.Register("WorkItemTypeMappings", typeof(ObservableCollection<WorkItemTypeMapping>), typeof(MainMediator), new UIPropertyMetadata(null));

        
        // For the current Destination project, this is the list of available work item types.  (Used to select the destination type in a move.)
        public List<WorkItemType> WorkItemTypesForCurrentDestProject
        {
            get { return (List<WorkItemType>)GetValue(WorkItemTypesForCurrentDestProjectProperty); }
            set { SetValue(WorkItemTypesForCurrentDestProjectProperty, value); }
        }
        public static readonly DependencyProperty WorkItemTypesForCurrentDestProjectProperty = DependencyProperty.Register("WorkItemTypesForCurrentDestProject", typeof(List<WorkItemType>), typeof(MainMediator), new UIPropertyMetadata(null));


        // Used to allow selection of default "title" column for workitem types:


        public ObservableCollection<WITSelectorProject> WITSelectorProjects
        {
            get { return (ObservableCollection<WITSelectorProject>)GetValue(WITSelectorProjectsProperty); }
            set { SetValue(WITSelectorProjectsProperty, value); }
        }

        // Using a DependencyProperty as the backing store for WITSelectorProjects.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty WITSelectorProjectsProperty =
            DependencyProperty.Register("WITSelectorProjects", typeof(ObservableCollection<WITSelectorProject>), typeof(MainMediator), new UIPropertyMetadata(null));


        #endregion

        #region Settings Pane
 
        public void SettingsWITDefaultColumnProjectSelected(WITSelectorProject project, RoutedPropertyChangedEventArgs<object> args)
        {
            if (WIStore == null)
                return;

            var wiTypes = WIStore.Projects[project.WITProjectName].WorkItemTypes;
            foreach (WorkItemType wiType in wiTypes)
            {
                // See if we already have a value for this workitem type
                WITSelectorNode savedNode = wiType.GetWITDefaultFieldFromMemory();

                // If we have a saved version of this node then use that.
                if (savedNode != null)
                {
                    // Update the field list and make sure that our saved default is in the list.
                    savedNode.UpdateFieldList(wiType);
                    // If our saved node no longer exists then revert to "Title"
                    if (!savedNode.AllFieldsForWorkItemType.Exists(field => field == savedNode.DefaultFieldName))
                        savedNode.DefaultFieldName = wiType.GetFieldDefinition("Title").Name;   

                }
                // This is a new node that we need to create.
                else
                {
                    string defaultField = wiType.GetFieldDefinition("Title").Name;

                    var newNode = new WITSelectorNode
                                      {
                                          DefaultFieldName = defaultField,
                                          WorkItemTypeName = wiType.Name,
                                          AllFieldsForWorkItemType = new List<string>()
                                      };
                    newNode.UpdateFieldList(wiType);
                    project.WITNodes.Add(newNode);
                }
            }
            // Save off any updates we have made
            WITSaveDefaultFieldsForToXML(Form.CurrentServer);
        }

        public void WITSaveDefaultFieldsForToXML(string server)
        {
            
            WITSelectorProjects.SerializeObject(server + "DefaultFields.xml");

        }

        #endregion Settings Pane
    }
}
